<template>
  <div class="pg-uploader-wrapper">

    <draggable class="d-flex" :sort="!is_disabled && multiple" v-model="list" @end="onEndDrag">
      <div
          class="pg-img-box"
          v-for="item in list"
          :key="item"
          @mouseenter="onMouseenter(item)"
          @mouseleave="onMouseleave"
      >
        <img
            v-if="type === 'image'"
            :src="item"
            class="file-img"
            width="54"
            height="54"
            alt=""
        />
        <img
            v-if="type === 'video'"
            class="file-img"
            width="54"
            height="54"
            alt=""
            src=""
        >
        <img
            v-if="type === 'apk'"
            class="file-img"
            width="54"
            height="54"
            alt=""
            src=""
        >
        <transition name="fade">
          <div class="pg-hover-mask" v-if="hover_item === item && (!is_disabled || type !== 'apk')">
            <pg-button flat circle size="sm" @click="onRemoveItem(item)" v-if="!is_disabled"><i class="icon-bin"></i></pg-button>
            <pg-button flat circle size="sm" @click="onPreviewItem(item)" v-if="type !== 'apk'"><i class="icon-search4"></i></pg-button>
          </div>
        </transition>
      </div>
    </draggable>

    <div class="pg-uploader" :class="{loading, disabled: is_disabled}" @click="onSelect" v-if="(multiple ? list.length < limit : list.length < 1)">
      <input type="file" :accept="accept" class="file-input" hidden @change="onChange" ref="file-input" :multiple="multiple">
      <i v-if="loading" class="icon-spinner3 spinner"></i>
      <i class="icon-plus2" v-else></i>
    </div>
  </div>
</template>

<script>

  import {findUpComponent} from './../_util/assist';
  import Http from './../../http/http';
  import Toast from './../toast';
  import Preview from './../preview';
  import pgButton from './../button/button';
  import { osConfig } from './../../pgyos.config';

  export default {
    name: "pg-uploader",
    components: {
      'draggable': window['vuedraggable'],
      'pg-button': pgButton,
    },
    props: {
      module: { type: String, default: 'item' },
      multiple: {type: Boolean, default: false},
      value: { type: String | Array, default: '' },
      type: { type: String, default: 'image' },
      limit: { type: Number, default: 5 },
      ratio: { type: String, default: '' }, // 格式(width*height): 750*1336
      disabled: { type: Boolean, default: false },
    },
    model: {
      prop: 'value',
      event: 'change'
    },

    computed: {
      is_disabled() {
        return this.$props.disabled || !!this.pgFormItem?.disabled;
      },
      accept() {
        switch (this.$props.type) {
          case 'apk':
            return '.apk';
          case 'video':
            return 'audio/mp4';
          default: 
            return 'image/*';
        }
      },
      ratios() {
        const { type, ratio } = this.$props;
        if (type !== 'image') return [];
        if (typeof ratio !== 'string') return [];

        return ratio.split("*").map(item => parseInt(item)).filter(item => !isNaN(item));
      }
    },

    watch: {
      value(v) {
        const {multiple} = this.$props;
        this.$data.list = multiple ? v.map(d => osConfig().cos_tenctent_path + d) 
                                   : v ? [osConfig().cos_tenctent_path + v] : [];
        this.pgFormItem?.sync?.(this.$data.list);
      }
    },

    data() {
      const {multiple, value} = this.$props;
      let v = [];
      if (multiple) {
        if (Array.isArray(value) && value.length > 0) {
          v = value.map(d => osConfig().cos_tenctent_path + d);
          this.pgFormItem?.sync?.(v);
        }
      } else {
        if (value) {
          v = [osConfig().cos_tenctent_path + value];
          this.pgFormItem?.sync?.(v);
        }
      }

      return {
        loading: false,
        list: v,
        hover_item: '',
      }
    },

    beforeCreate() {
      this.pgFormItem = findUpComponent(this, 'pg-form-item');
    },

    methods: {

      onSelect(e) {
        if (this.is_disabled) return;

        if (this.$data.loading) return;
        this.$refs['file-input']?.click?.();
      },

      onChange(event) {
        
        let files = event.target.files;
        files = [...files]; // files 类数组 需要进行转化。

        const { multiple, value, limit } = this.$props;

        if (multiple && ((limit - value.length) < files.length)) {
          Toast({ type: 'warning', message: `最多还能选择${limit - value.length}张图片` });
          return;
        }

        // 清空选择过的图片
        event.target.value = '';

        // 如果没有 比例规格 配置
        if (this.ratios.length !== 2) {
          this.tencentPresignedUrl(files);

        } else {

          Promise.allSettled(files.map(file => this.specsCheck(file)))
              .then(results => {
                const success_list = results.filter(item => item.status === 'fulfilled').map(item => item.value);
                const fail_list = results.filter(item => item.status === 'rejected');

                success_list.length > 0 && this.tencentPresignedUrl(success_list);

                if (fail_list.length > 0) {
                  if (fail_list.length === files.length) {
                    Toast({ type: 'warning', message: `所选图片不符合规格` });
                  } else {
                    Toast({ type: 'warning', message: `${fail_list.length} 张图片不符合规格` });
                  }
                }
              });

        }

      },

      specsCheck(file) {
        return new Promise((resolve, reject) => {

          const reader = new FileReader();
          reader.onload = (e) => {
            const data = e.target.result;
            const image = new Image();
            image.onload = () => {
              const width = image.width;
              const height = image.height;
              if (width === this.ratios[0] && height === this.ratios[1]) {
                return resolve(file);
              }
              reject();
            };
            image.src= data;
          };

          reader.readAsDataURL(file);

        });

      },

      // 获取到腾讯云的上传url
      tencentPresignedUrl(files) {
        this.$data.loading = true;
        Promise.all(files.map(file => {
          return Http.get(osConfig().cos_presigned_api, {module: this.$props.module})
          .then(res => {
            let formData = {
              file: file,
              key: res.data.key,
              presigned_url: res.data.presigned_url
            };

            // 为apk 文件自动添加后缀
            if (this.$props.type === 'apk') {
              let split_urls = formData.presigned_url.split('?');
              formData.presigned_url = split_urls[0] + '.apk?' + split_urls[1];
              formData.key = formData.key + '.apk';
            }

            return this.upTencentPath(formData);
          })
        })).then(list => {
            this.$data.loading = false;
            let d = this.$props.multiple ? [...this.$props.value, ...list.map(d => d.data)] : list[0].data;
            this.$emit('change', d);
        }).catch(e => {
            this.$data.loading = false;
            Toast({ type: 'danger', message: '图片上传失败' });
          });
      },

      upTencentPath(data) {
        return new Promise((resolve, reject) => {
          var xhr = new XMLHttpRequest();
          xhr.onreadystatechange = function (e) {
            if (xhr.readyState === 4) {
              if (xhr.status === 200) {
                resolve({ code: 0, data: data.key });
              } else {
                reject({ code: 4, message: '上传失败！请重试...', data: null });
              }
            }
          };
          xhr.open('PUT', data.presigned_url, false);
          xhr.send(data.file);
        });
      },

      onEndDrag() {
        let list = this.$data.list.map(item => item.substring(osConfig().cos_tenctent_path.length));
        this.$emit('change', list);
      },

      onMouseenter(item) {
        this.$data.hover_item = item;
      },

      onMouseleave() {
        this.$data.hover_item = '';
      },

      onRemoveItem(item) {
        const { multiple, value } = this.$props;
        if (multiple) {
          this.$emit('change', value.filter(d => (osConfig().cos_tenctent_path + d) !== item));
        } else {
          this.$emit('change', '');
        }
      },

      onPreviewItem(item) {
        Preview({ src: item, list: this.$data.list, type: this.$props.type });
      },
    }
  }
</script>

<style scoped>

</style>